package asteroid

import (
	"strings"

	"gddgame.cc/galaxy/utils/def"
	"github.com/pkg/errors"
)

// 获取网关，获取本地服务列表
type ProxyRouter interface {
	// 根据目标分析得到对应的网关列表
	Wans(key string) []string
	// 是否是本地网关
	IsLocal(wan string) bool
	// 解析key，得到所有node
	Resolve(key string) []string
}
type ProxyWorker interface {
	// 是否是本地网关
	IsLocal(wan string) bool
	// 获取内部代理路由地址
	Proxy() string
	// 获取代理网关地址
	Wan() string
	// 生成key
	Generate(keys []string) string
}
type Proxy struct {
	layer *Layer

	router ProxyRouter
	worker ProxyWorker
}

func (p *Proxy) BindRouter(router ProxyRouter) {
	p.router = router
}
func (p *Proxy) BindWorker(worker ProxyWorker) {
	p.worker = worker
}

// 直接发送到对应代理服务器
func (p *Proxy) forwards(logic string, key []string, stream def.Stream) error {
	if p.worker == nil {
		return errors.New("Worker is miss")
	}
	return p.forward(logic, p.worker.Generate(key), stream)
}
func (p *Proxy) forward(logic string, key string, stream def.Stream) error {
	if p.worker == nil {
		return errors.New("Worker is miss")
	}
	var msg *Msg
	// 解析logic
	if strings.Contains(logic, ":") {
		// 直接发送到对方代理节点
		msg = newMsg(ForwardProxyMessageType, "", key)
		msg.to = []string{logic}
		msg.layer = p.layer
		if !msg.layer.socket.IsConnect(logic) {
			_ = msg.layer.ConnectNode(logic)
		}
	} else {
		// 发送到本地代理节点解析
		msg = newMsg(FindProxyMessageType, logic, key)
		msg.to = []string{p.worker.Proxy()}
		msg.layer = p.layer
	}
	msg.From = p.layer.node
	msg.stream = stream
	return msg.Send(ProxyMessage, nil)
}

func (p *Proxy) backward(req *Msg, res *Msg) string {
	res.to = []string{req.From}
	if req.Proxy == "" || p.router.IsLocal(req.Proxy) {
		// 本地网关，直接发回from
		res.Proxy = ""
		return req.From
	} else {
		// 设定回源网关
		res.Proxy = p.worker.Wan()
		res.cross = req.Proxy
		// 代理通过key进行解析节点
		return req.Proxy
	}
}

func (p *Proxy) dispatch(msg *Msg) bool {
	if p.router == nil {
		p.layer.Error(errors.New("Router is miss"))
		return false
	}
	switch msg.Type {
	case FindProxyMessageType:
		wans := p.router.Wans(msg.Logic)
		for _, wan := range wans {
			if p.router.IsLocal(wan) {
				nodes := p.router.Resolve(msg.Key)
				for _, node := range nodes {
					if node == p.layer.node {
						p.layer.dispatch(msg.PayloadData)
					} else {
						i := p.layer.socket.Get(node)
						if i != nil {
							i <- msg
						}
					}
				}
			} else {
				if err := p.forward(wan, msg.Key, msg); err != nil {
					p.layer.Error(err)
				}
			}
		}
	case ForwardProxyMessageType:
		nodes := p.router.Resolve(msg.Key)
		//fmt.Println(msg.Proxy, msg.Key, msg.Logic, nodes, p.layer.node)
		for _, node := range nodes {
			if node == p.layer.node {
				p.layer.dispatch(msg.PayloadData)
			} else {
				i := p.layer.socket.Get(node)
				if i != nil {
					i <- msg
				}
			}
		}
	}
	return true
}
